<template>
  <div class="audio-recorder">
    <div class="controls">
      <button @click="toggleRecording" :disabled="isProcessing && !isRecording">
        {{ isRecording ? '停止录音' : '开始录音' }}
      </button>
      <div class="storage-toggle">
        <label class="switch">
          <input type="checkbox" v-model="useLocalStorage">
          <span class="slider round"></span>
        </label>
        <span>{{ useLocalStorage ? '本地API' : '阿里云API' }}</span>
      </div>
      <div class="streaming-toggle">
        <label class="switch">
          <input type="checkbox" v-model="useStreaming">
          <span class="slider round"></span>
        </label>
        <span>{{ useStreaming ? '流式输出' : '完整输出' }}</span>
      </div>
    </div>
    <div v-if="isRecording" class="recording-indicator">
      正在录音... {{ recordingTime }}秒
    </div>
    <div v-if="isProcessing" class="processing-indicator">
      正在处理...
    </div>
    <div v-if="transcriptionResult" class="result">
      <h3>识别结果:</h3>
      <p @click="playTextToSpeech(transcriptionResult)" class="clickable-text">{{ transcriptionResult }}</p>
      <audio v-if="audioUrl" :src="audioUrl" controls class="audio-player"></audio>
    </div>
    <div v-if="wsStatus" class="ws-status" :class="{ 'ws-connected': wsConnected }">
      WebSocket状态: {{ wsStatus }}
    </div>



    <!-- 新增聊天界面 -->
    <div class="chat-container">
      <h3>AI 对话</h3>
      <div class="chat-messages" ref="chatMessagesRef">
        <div v-for="(message, index) in chatMessages" :key="index" 
             :class="['message', message.type === 'user' ? 'user-message' : 'ai-message']">
          <div class="message-content">
            {{ message.content }}
          </div>
          <div class="message-time">{{ message.time }}</div>
        </div>
      </div>
      <div class="chat-input">
        <input 
          type="text" 
          v-model="chatInput" 
          placeholder="输入消息..."
          @keyup.enter="sendMessage"
          :disabled="isAiResponding"
        />
        <button @click="sendMessage" :disabled="isAiResponding">
          {{ isAiResponding ? 'AI正在回复...' : '发送' }}
        </button>
      </div>
    </div>

       <!-- 新增文字转语音输入框和按钮 -->
       <div class="tts-container">
      <h3>文字转语音:</h3>
      <div class="tts-input-group">
        <input 
          type="text" 
          v-model="ttsText" 
          placeholder="请输入要转换为语音的文字"
          class="tts-input"
        />
        <button @click="playTextToSpeech(ttsText)" class="tts-button">
          生成语音
        </button>
      </div>
      <div v-if="!audioUrl" class="tts-hint">请点击生成语音</div>
      <audio v-if="audioUrl" :src="audioUrl" controls class="audio-player"></audio>
    </div>
  </div>
   
</template>

<script setup>
import { ref, onMounted, onUnmounted, nextTick } from 'vue';
import axios from 'axios';

// 组件状态
const isRecording = ref(false);
const isProcessing = ref(false);
const transcriptionResult = ref('');
const recordingTime = ref(0);
const useLocalStorage = ref(true); // 默认使用本地API
const useStreaming = ref(true); // 默认使用流式输出(WebSocket)
let recordingTimer = null;
let streamingTimer = null;
const streamingInterval = 3000; // 每3秒处理一次录音数据
const minStreamingSize = 8000; // 最小流式处理数据大小（字节）

// 文字转语音相关
const ttsText = ref('');
const audioUrl = ref(null);

// WebSocket相关
let webSocket = null;
let sessionId = null;
const wsConnected = ref(false);
const wsStatus = ref('未连接');

// 录音相关变量
let mediaRecorder = null;
const audioChunks = ref([]);
const streamingChunks = ref([]);

// 新增聊天相关状态
const chatMessages = ref([]);
const chatInput = ref('');
const isAiResponding = ref(false);
const chatMessagesRef = ref(null);

// 初始化
onMounted(() => {
  setupAudioRecorder();
  connectWebSocket();
});

// 组件卸载时清理
onUnmounted(() => {
  stopRecording();
  clearInterval(recordingTimer);
  clearInterval(streamingTimer);
  disconnectWebSocket();
});

// 连接WebSocket
const connectWebSocket = () => {
  try {
    // 创建WebSocket连接
    webSocket = new WebSocket('ws://localhost:9999/ws/voice');

    // 连接建立时的处理
    webSocket.onopen = (event) => {
      wsConnected.value = true;
      wsStatus.value = '已连接';
      console.log('WebSocket连接已建立');
    };

    // 接收消息的处理
    webSocket.onmessage = (event) => {
      try {
        const message = JSON.parse(event.data);
        console.log('收到WebSocket消息:', message);

        if (message.type === 'connection') {
          sessionId = message.sessionId;
          console.log('会话ID:', sessionId);
        } else if (message.type === 'recognition') {
          // 处理识别结果
          if (message.text !== undefined) {
            transcriptionResult.value = message.text;
            // 自动填充到文字转语音输入框
            ttsText.value = message.text;
            // 自动填充到聊天输入框
            chatInput.value = message.text;
          }

          // 如果是最终结果，确保重置处理状态
          if (message.isFinal) {
            setTimeout(() => {
              isProcessing.value = false;
            }, 500);
            console.log('收到最终识别结果，重置处理状态');
          }
        } else if (message.type === 'error') {
          console.error('WebSocket错误:', message.message);
          if (!isRecording.value) {
            transcriptionResult.value = '错误: ' + message.message;
          }
          // 发生错误时也重置处理状态
          isProcessing.value = false;
        }
      } catch (e) {
        console.error('解析WebSocket消息失败:', e, event.data);
        // 解析失败时也重置处理状态
        isProcessing.value = false;
      }
    };

    // 错误处理
    webSocket.onerror = (event) => {
      console.error('WebSocket发生错误:', event);
      wsConnected.value = false;
      wsStatus.value = '连接错误';
    };

    // 连接关闭的处理
    webSocket.onclose = (event) => {
      console.log('WebSocket连接已关闭:', event.code, event.reason);
      wsConnected.value = false;
      wsStatus.value = '已断开';

      // 可以在这里添加自动重连逻辑
      if (isRecording.value) {
        setTimeout(connectWebSocket, 2000); // 2秒后尝试重连
      }
    };
  } catch (e) {
    console.error('建立WebSocket连接时出错:', e);
    wsStatus.value = '连接错误: ' + e.message;
  }
};

// 断开WebSocket连接
const disconnectWebSocket = () => {
  if (webSocket && webSocket.readyState === WebSocket.OPEN) {
    webSocket.close();
    wsConnected.value = false;
    wsStatus.value = '已断开';
    console.log('已断开WebSocket连接');
  }
};

// 设置音频录制器
const setupAudioRecorder = async () => {
  try {
    const stream = await navigator.mediaDevices.getUserMedia({ audio: true });

    // 尝试使用更好的音频格式
    const options = {};
    if (MediaRecorder.isTypeSupported('audio/wav')) {
      options.mimeType = 'audio/wav';
    } else if (MediaRecorder.isTypeSupported('audio/webm')) {
      options.mimeType = 'audio/webm';
    }

    mediaRecorder = new MediaRecorder(stream, options);

    // 当有录音数据可用时
    mediaRecorder.ondataavailable = (event) => {
      if (event.data.size > 0) {
        audioChunks.value.push(event.data);
        // 如果是流式处理模式，也添加到流式处理数组
        if (useStreaming.value) {
          streamingChunks.value.push(event.data);
        }
      }
    };

    // 录音停止时处理数据
    mediaRecorder.onstop = () => {
      if (!useStreaming.value) {
        processRecording();
      }
    };

    console.log('音频录制器已设置，使用MIME类型:', mediaRecorder.mimeType);
  } catch (error) {
    console.error('设置音频录制器时出错:', error);
    alert('无法访问麦克风，请确保已授予权限。');
  }
};

// 切换录音状态
const toggleRecording = () => {
  if (isRecording.value) {
    stopRecording();
  } else {
    startRecording();
  }
};

// 开始录音
const startRecording = async () => {
  try {
    transcriptionResult.value = '';
    audioChunks.value = [];
    streamingChunks.value = [];
    isRecording.value = true;
    recordingTime.value = 0;

    // 如果WebSocket未连接，尝试重新连接
    if ((!wsConnected.value || !webSocket || webSocket.readyState !== WebSocket.OPEN) && useStreaming.value) {
      connectWebSocket();
    }

    // 首先尝试获取用户的媒体流
    const mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true });
    mediaRecorder = new MediaRecorder(mediaStream);

    // 媒体数据可用时收集数据
    mediaRecorder.ondataavailable = (event) => {
      if (event.data.size > 0) {
        audioChunks.value.push(event.data);
        // 如果是流式处理模式，也添加到流式处理数组
        if (useStreaming.value) {
          streamingChunks.value.push(event.data);
        }
      }
    };

    // 开始录音
    mediaRecorder.start(100);

    console.log('开始录音...');

    // 如果开启了流式输出，设置定时器定期处理数据
    if (useStreaming.value) {
      streamingTimer = setInterval(() => {
        processNativeWebSocketStreaming(false);
      }, streamingInterval);
    }

    // 开始计时
    recordingTimer = setInterval(() => {
      recordingTime.value++;
    }, 1000);
  } catch (error) {
    console.error('录音失败:', error);
    transcriptionResult.value = '无法访问麦克风: ' + error.message;
    isRecording.value = false;
  }
};

// 停止录音
const stopRecording = async () => {
  if (!isRecording.value) return;

  if (mediaRecorder && mediaRecorder.state !== 'inactive') {
    mediaRecorder.stop();
    console.log('停止录音');
  }

  isRecording.value = false;
  clearInterval(recordingTimer);

  // 如果是流式处理，清除流式处理定时器
  if (useStreaming.value) {
    clearInterval(streamingTimer);
    // 处理最后一批数据
    if (audioChunks.value.length > 0) {
      console.log('处理最终音频数据，共 ' + audioChunks.value.length + ' 个片段');
      processNativeWebSocketStreaming(true);
    } else {
      // 确保如果没有数据也可以重置处理状态
      isProcessing.value = false;
    }
    return;
  }

  // 非流式模式下，等待所有音频数据收集完毕后再处理
  setTimeout(() => {
    processRecording();
  }, 500);
};

// 使用原生WebSocket处理流式音频数据
const processNativeWebSocketStreaming = async (isFinal = false) => {
  if ((!isRecording.value && !isFinal) || !wsConnected.value || !webSocket || webSocket.readyState !== WebSocket.OPEN) return;

  // 确保有音频数据
  if (audioChunks.value.length === 0) {
    console.log('没有音频数据可处理');
    // 如果没有数据并且是最终调用，重置处理状态
    if (isFinal) {
      isProcessing.value = false;
    }
    return;
  }

  console.log(`通过WebSocket处理${isFinal ? '最终' : '流式'}音频数据，累计${audioChunks.value.length}个片段`);

  // 创建完整的音频blob
  const audioBlob = new Blob(audioChunks.value, { type: 'audio/wav' });

  // 如果blob太小且不是最终数据，则跳过处理
  if (audioBlob.size < minStreamingSize && !isFinal) {
    console.log(`音频数据太小 (${audioBlob.size} bytes)，跳过处理`);
    return;
  }

  // 只在非final状态下设置处理中，这样可以保证最终状态下重置按钮状态
  if (!isFinal) {
    isProcessing.value = true;
  }

  try {
    // 将音频数据转换为Base64
    const reader = new FileReader();
    reader.readAsDataURL(audioBlob);
    reader.onloadend = function () {
      const base64Audio = reader.result;

      // 如果数据超过1MB，分片发送
      const maxChunkSize = 1 * 1024 * 1024; // 1MB

      if (base64Audio.length > maxChunkSize) {
        // 分片发送大文件
        console.log(`音频数据较大 (${base64Audio.length} bytes)，分片发送`);

        // 只发送最近的一部分数据进行识别
        // 对于流式处理，我们可以只发送最近的一段数据
        if (!isFinal) {
          // 非最终数据，只发送一段
          sendAudioChunk(base64Audio.slice(-maxChunkSize), isFinal);
        } else {
          // 最终数据，发送完整数据（但可能会有连接问题）
          // 分片发送
          const totalChunks = Math.ceil(base64Audio.length / maxChunkSize);
          console.log(`分为 ${totalChunks} 个片段发送`);

          // 只发送最后一段数据
          sendAudioChunk(base64Audio.slice(-maxChunkSize), isFinal);
        }
      } else {
        // 直接发送小文件
        sendAudioChunk(base64Audio, isFinal);
      }

      // 如果是最终数据，在发送后重置处理状态
      if (isFinal) {
        // 延迟重置，给服务器一些处理时间
        setTimeout(() => {
          isProcessing.value = false;
        }, 1000);
      }
    };
  } catch (err) {
    console.error('WebSocket处理音频失败:', err);
    isProcessing.value = false;
  }
};

// 发送音频数据片段
const sendAudioChunk = (audioData, isFinal) => {
  // 准备发送给WebSocket的消息
  const message = {
    type: 'audio',
    audioData: audioData,
    useLocalStorage: useLocalStorage.value,
    isFinal: isFinal
  };

  // 发送到WebSocket
  if (webSocket && webSocket.readyState === WebSocket.OPEN) {
    webSocket.send(JSON.stringify(message));
    console.log('音频数据已通过WebSocket发送', isFinal ? '(最终数据)' : '');
  } else {
    console.error('WebSocket未连接，无法发送数据');
    isProcessing.value = false;
    if (!wsConnected.value) {
      // 尝试重新连接
      connectWebSocket();
    }
  }
};

// 处理完整录音（非WebSocket方式）
const processRecording = async () => {
  if (audioChunks.value.length > 0) {
    isProcessing.value = true;

    // 创建一个FormData对象
    const formData = new FormData();

    // 将录音数据作为文件添加到FormData
    const audioBlob = new Blob(audioChunks.value, { type: 'audio/wav' });
    formData.append('file', audioBlob, 'recording.wav');
    
    // 添加存储模式参数
    formData.append('useLocalStorage', useLocalStorage.value ? 'true' : 'false');
    formData.append('isStreaming', 'false');

    // 发送录音数据到后端进行处理
    try {
      const response = await axios.post('http://localhost:9999/api/voice/transcribe', formData, {
      headers: {
        'Content-Type': 'multipart/form-data'
      }
      });

      isProcessing.value = false;
      console.log('处理结果:', response.data);

      try {
        if (typeof response.data === 'string') {
          // 尝试解析JSON字符串
          const jsonData = JSON.parse(response.data);

          // 检查是否有错误信息
          if (jsonData.error) {
            transcriptionResult.value = jsonData.error;
            return;
          }

          // 发送处理后的文本
          const recognizedText = jsonData.text || jsonData;
          transcriptionResult.value = recognizedText;
          // 自动填充到文字转语音输入框
          ttsText.value = recognizedText;
          // 自动填充到聊天输入框
          chatInput.value = recognizedText;
        } else {
          // 如果已经是对象，直接使用
          if (response.data.error) {
            transcriptionResult.value = response.data.error;
            return;
          }

          const recognizedText = response.data.text || response.data;
          transcriptionResult.value = recognizedText;
          // 自动填充到文字转语音输入框
          ttsText.value = recognizedText;
          // 自动填充到聊天输入框
          chatInput.value = recognizedText;
        }
      } catch (e) {
        // 如果不是JSON，直接发送原始文本
        transcriptionResult.value = response.data;
        // 自动填充到文字转语音输入框
        ttsText.value = response.data;
        // 自动填充到聊天输入框
        chatInput.value = response.data;
      }
    } catch (error) {
      isProcessing.value = false;
      console.error('处理录音时出错:', error);
      transcriptionResult.value = error.message || '处理录音时出错';
    }
  }
};

// 添加文字转语音功能
const playTextToSpeech = async (text) => {
  if (!text) {
    alert('请输入要转换的文字');
    return;
  }
  
  try {
    // 修改为使用新的接口和数据格式
    const ttsRequestData = {
      tts_text: text,
      mode: '预训练音色',
      speed: 1.0,
      sft_speaker: '中文女'
    };

    const response = await axios.post('http://localhost:8233/tts', ttsRequestData, {
      responseType: 'blob'
    });

    // 检查音频数据大小
    const blob = new Blob([response.data], { type: 'audio/wav' });
    
    console.log('音频数据大小:', blob.size, '字节');
    if (blob.size < 1000) {
      alert('生成的音频数据异常，可能不完整');
      return;
    }
    
    // 创建音频URL
    audioUrl.value = URL.createObjectURL(blob);
    
    // 自动播放音频
    const audio = new Audio(audioUrl.value);
    audio.onended = () => {
      // 音频播放完成后释放资源
      URL.revokeObjectURL(audioUrl.value);
    };
    
    audio.play().catch(error => {
      console.error('自动播放失败:', error);
      alert('自动播放失败，请允许网站自动播放音频');
    });
  } catch (error) {
    console.error('文字转语音失败:', error);
    alert('文字转语音失败: ' + error.message);
  }
};

// 格式化时间
const formatTime = () => {
  const now = new Date();
  return now.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' });
};

// 滚动到最新消息
const scrollToBottom = () => {
  nextTick(() => {
    if (chatMessagesRef.value) {
      chatMessagesRef.value.scrollTop = chatMessagesRef.value.scrollHeight;
    }
  });
};

// 发送消息到AI
const sendMessage = async () => {
  if (!chatInput.value.trim() || isAiResponding.value) return;

  const userMessage = chatInput.value.trim();
  chatInput.value = '';

  // 确保 chatMessages.value 是数组
  if (!Array.isArray(chatMessages.value)) {
    chatMessages.value = [];
  }

  // 添加用户消息到聊天记录
  chatMessages.value = [...chatMessages.value, {
    type: 'user',
    content: userMessage,
    time: formatTime()
  }];

  // 滚动到最新消息
  scrollToBottom();

  isAiResponding.value = true;

  try {
    // 调用AI接口
    const response = await axios.get('http://localhost:9999/ai/ollama', {
      params:{
        message: userMessage
      }
    });

    // 添加AI回复到聊天记录
    const aiResponse = response.data || '抱歉，我无法回答这个问题。';
    chatMessages.value = [...chatMessages.value, {
      type: 'ai',
      content: aiResponse,
      time: formatTime()
    }];

    // 自动将AI回复同步到文字转语音输入框
    ttsText.value = aiResponse;

    // 滚动到最新消息
    scrollToBottom();

  } catch (error) {
    console.error('AI对话出错:', error);
    const errorMessage = '抱歉，发生了错误，请稍后再试。';
    chatMessages.value = [...chatMessages.value, {
      type: 'ai',
      content: errorMessage,
      time: formatTime()
    }];
    
    // 同样将错误消息同步到文字转语音输入框
    ttsText.value = errorMessage;
    
    // 滚动到最新消息
    scrollToBottom();
  } finally {
    isAiResponding.value = false;
  }
};
</script>

<style scoped>
.audio-recorder {
  padding: 20px;
  border-radius: 8px;
  background-color: #f9f9f9;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  max-width: 500px;
  margin: 0 auto;
}

.controls {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 15px;
  flex-wrap: wrap;
  gap: 10px;
}

button {
  padding: 10px 20px;
  background-color: #4CAF50;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-size: 16px;
  transition: background-color 0.3s;
  flex: 1;
  min-width: 120px;
}

button:hover {
  background-color: #45a049;
}

button:disabled {
  background-color: #cccccc;
  cursor: not-allowed;
}

.recording-indicator {
  color: #ff0000;
  font-weight: bold;
  margin: 10px 0;
  animation: blink 1s infinite;
}

.processing-indicator {
  color: #0066cc;
  font-weight: bold;
  margin: 10px 0;
}

.result {
  margin-top: 20px;
  padding: 15px;
  background-color: #e9f7ef;
  border-radius: 4px;
  border-left: 4px solid #4CAF50;
}

.result h3 {
  margin-top: 0;
  color: #2e7d32;
}

.ws-status {
  margin-top: 10px;
  font-size: 12px;
  color: #777;
}

.ws-connected {
  color: #4CAF50;
}

@keyframes blink {
  50% {
    opacity: 0.5;
  }
}

/* 开关样式 */
.storage-toggle,
.streaming-toggle {
  display: flex;
  align-items: center;
  gap: 10px;
}

.switch {
  position: relative;
  display: inline-block;
  width: 60px;
  height: 34px;
}

.switch input {
  opacity: 0;
  width: 0;
  height: 0;
}

.slider {
  position: absolute;
  cursor: pointer;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: #ccc;
  transition: .4s;
}

.slider:before {
  position: absolute;
  content: "";
  height: 26px;
  width: 26px;
  left: 4px;
  bottom: 4px;
  background-color: white;
  transition: .4s;
}

input:checked+.slider {
  background-color: #2196F3;
}

input:focus+.slider {
  box-shadow: 0 0 1px #2196F3;
}

input:checked+.slider:before {
  transform: translateX(26px);
}

.slider.round {
  border-radius: 34px;
}

.slider.round:before {
  border-radius: 50%;
}

.clickable-text {
  cursor: pointer;
  padding: 8px;
  border-radius: 4px;
  transition: background-color 0.3s;
}

.clickable-text:hover {
  background-color: #e0e0e0;
}

.audio-player {
  margin-top: 10px;
  width: 100%;
}

.tts-container {
  margin-top: 20px;
  padding: 15px;
  background-color: #f0f8ff;
  border-radius: 4px;
  border-left: 4px solid #4169e1;
}

.tts-input-group {
  display: flex;
  margin-bottom: 10px;
}

.tts-input {
  flex: 1;
  padding: 8px 12px;
  border: 1px solid #ccc;
  border-radius: 4px 0 0 4px;
  font-size: 16px;
}

.tts-button {
  padding: 8px 15px;
  background-color: #4169e1;
  color: white;
  border: none;
  border-radius: 0 4px 4px 0;
  cursor: pointer;
  transition: background-color 0.3s;
  min-width: auto;
}

.tts-button:hover {
  background-color: #3a5fcd;
}

.tts-hint {
  color: #666;
  font-style: italic;
  margin: 10px 0;
}

/* 新增聊天界面样式 */
.chat-container {
  margin-top: 20px;
  padding: 15px;
  background-color: #fff;
  border-radius: 8px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.chat-messages {
  height: 300px;
  overflow-y: auto;
  padding: 10px;
  background-color: #f5f5f5;
  border-radius: 4px;
  margin-bottom: 10px;
}

.message {
  margin-bottom: 10px;
  max-width: 80%;
}

.message-content {
  padding: 8px 12px;
  border-radius: 12px;
  word-wrap: break-word;
}

.user-message {
  margin-left: auto;
}

.user-message .message-content {
  background-color: #007bff;
  color: white;
}

.ai-message {
  margin-right: auto;
}

.ai-message .message-content {
  background-color: #e9ecef;
  color: #212529;
}

.message-time {
  font-size: 12px;
  color: #6c757d;
  margin-top: 4px;
  text-align: right;
}

.chat-input {
  display: flex;
  gap: 10px;
}

.chat-input input {
  flex: 1;
  padding: 8px 12px;
  border: 1px solid #ced4da;
  border-radius: 4px;
  font-size: 16px;
}

.chat-input button {
  padding: 8px 15px;
  background-color: #28a745;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  transition: background-color 0.3s;
}

.chat-input button:disabled {
  background-color: #6c757d;
  cursor: not-allowed;
}

.chat-input button:hover:not(:disabled) {
  background-color: #218838;
}
</style> 